from selenium.webdriver.edge.options import Options
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
import logging
import pandas as pd
import time
import os
from selenium.webdriver.common.action_chains import ActionChains
from parsel import Selector
from config import Config
from datetime import datetime
import traceback



# 设置日志格式并且实例化对象
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)


# 初始化driver对象
def init_driver(is_show = False):
    
    # 设置Edge浏览器选项
    edge_options = Options()
    edge_options.add_argument("--start-maximized")  # 启动浏览器即最大化
    if not is_show:
        edge_options.add_argument("--headless")  # 无头模式
    # 实例化driver对象
    driver = webdriver.Edge(options=edge_options)
    # 隐式等待
    driver.implicitly_wait(10)
    # 绕过自动化检测
    driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
        "source": """
        Object.defineProperty(navigator, 'webdriver', {
        get: () => undefined
        })
        """
    })
    return driver

# 登录逻辑
def login(driver):
    # 默认启动训练任务的URL登录

    # 训练任务对应的url
    driver.get(Config.TRAIN_URL)
    # 开始模拟登录
    # 输入账号密码
    UP_inputs = driver.find_elements(By.CSS_SELECTOR, '.ant-input')
    time.sleep(0.1)
    UP_inputs[0].send_keys(Config.USERNAME)
    time.sleep(0.1)
    UP_inputs[1].send_keys(Config.PASSWORD)
    # 同意霸王条款 
    time.sleep(0.1)
    driver.find_element(By.CSS_SELECTOR, '.ant-checkbox-input').click()
    # 开始登录
    time.sleep(0.1)
    driver.find_elements(By.CSS_SELECTOR, '.ant-row.ant-form-item-row')[-1].click()
    time.sleep(0.5)
    if driver.find_elements(By.XPATH, f"//span[contains(text(),'登录成功')]"):
        # 等待登录完成
        logger.info("登录成功")
        return True
    else:
        logger.info("登录失败")
        return False
1
# 得到DataFrame
def get_df(data_list):
    # 将数据转换为DataFrame
    df = pd.DataFrame(data_list)
    return df


# 将数据存储为csv数据
def save_data(data_list, data_name):
    """
    :param data_list: 数据列表,保存只保存为csv
    :param data_name: 数据名称
    """
    if type(data_list) != type(pd.DataFrame()):
        df = get_df(data_list)
        
    df.to_csv(f"data\\{data_name}.csv", index=False, encoding='utf-8-sig')

def read_data(data_name):
    """
    :param data_name: 数据名称,只需要输入名称
    :return: 数据列表
    """
    df = pd.read_csv(f"data\\{data_name}.csv")
    data_list = df.to_dict('records')
    return data_list


def get_train_tasks(driver):
    # 获取当前页面的所有训练任务
    # 请求训练页面的URL
    driver.get(Config.TRAIN_URL)

    # 对应的训练任务列表
    train_task_list = []

    # 获取当前页面的所有训练任务
    while True:
        tasks_list = driver.find_elements(By.CSS_SELECTOR, '.ant-spin-container .card___oQ1yu')
        # 显示等待标签出现
        WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, '.ant-spin-container .card___oQ1yu .runValue___SiRPp')))
        # 开始获取训练任务信息
        for task in tasks_list:
            #　取出所有状态的文本
            status = [s.text for s in task.find_elements(By.CSS_SELECTOR, '.ant-tagv5')]
            # 运行时间
            doing_time = task.find_element(By.CSS_SELECTOR, '.runValue___SiRPp').text
            # 其他信息
            info = [s.text for s in task.find_elements(By.CSS_SELECTOR, '.ant-completeText-moreText-moreText')]
            # 预训练了
            if len(info) == 5:
                # 任务名
                name_info = info[0]
                # 框架名
                framework_info = info[1]
                # 算法名
                algorithm_info = info[2]
                # 预训练模型
                pretrain_info = info[3]
                # 提交者
                submitter_info = info[4]
            else:
                # 任务名
                name_info = info[0]
                # 框架名
                framework_info = info[1]
                # 算法名
                algorithm_info = info[2]
                # 提交者
                submitter_info = info[3]
            
            # 描述信息
            desc = task.find_element(By.CSS_SELECTOR, '.ant-typography.ant-typography-ellipsis.ant-typography-single-line.ant-copyableText').text
            train_task_list.append({
                'status': status,
                'time': doing_time,
                'name_info': name_info,
                'framework_info': framework_info,
                'algorithm_info': algorithm_info,
                'pretrain_info': pretrain_info,
                'submitter_info': submitter_info,
                'desc': desc
            })

        # 检测是否可以继续下一页
        next_page_tag = driver.find_element(By.CSS_SELECTOR, '.ant-pagination-next')
        if next_page_tag.get_attribute('aria-disabled') == 'false':
            next_page_tag.click()
            time.sleep(1)
        else:
            break

    logger.info(f'共获取到{len(train_task_list)}条训练任务信息')
    #　保存数据
    save_data(train_task_list, 'train_task_info')

    return True

def get_monitor_url(driver):
    # 请求对应页面    
    driver.get(Config.TRAIN_URL)
    time.sleep(1)
    # 初始化列表
    monitor_url = ''
    # 获取正在进行的任务
    doing_task = driver.find_elements(By.CSS_SELECTOR, '.ant-spin-container .card___oQ1yu')[0]
    # 对应的按钮
    doing_botton = doing_task.find_elements(By.CSS_SELECTOR, '.ant-btn.ant-btn-default')
    # 获取监控URL
    doing_botton[0].click()
    # 切换到对应的窗口
    driver.switch_to.window(driver.window_handles[-1])
    # 获取监控URL
    logger.info(f"监控URL: {driver.current_url}")
    monitor_url = driver.current_url
    # 切换回去
    driver.switch_to.window(driver.window_handles[0])
    return monitor_url

# 获取对应的模型标签
def get_model_list(driver, id=1):
    """
    获取模型列表
    :param driver: 浏览器驱动
    :param id: 第几个任务，从第一页从上往下一直数，默认第一个
    :return: 模型列表
    """
    # 请求对应页面    
    driver.get(Config.TRAIN_URL)
    time.sleep(1)
    # 存储模型信息
    model_dict_list = []
    # 获取正在进行的任务
    doing_task = driver.find_elements(By.CSS_SELECTOR, '.ant-spin-container .card___oQ1yu')[id-1]
    # 对应的按钮
    doing_botton = doing_task.find_elements(By.CSS_SELECTOR, '.ant-btn.ant-btn-default')
    # 进行点击
    doing_botton[1].click()
    
    # 停顿
    time.sleep(0.5)
    while True:
        # 获取所有模型的标签
        model_tags = driver.find_elements(By.CSS_SELECTOR, '.ant-table-tbody>tr')
        for i in range(1, len(model_tags)):
            model_dict = {}
            info_tag = model_tags[i].find_elements(By.CSS_SELECTOR, '.ant-table-cell')
            train_time = info_tag[0]
            train_steps = info_tag[1]
            model_size = info_tag[2]
            bottom_tag = info_tag[3]
            model_dict['train_time'] = train_time.text
            model_dict['train_steps'] = train_steps.text
            model_dict['model_size'] = model_size.text
            model_dict['dnowload_tag'] = bottom_tag.find_elements(By.CSS_SELECTOR, '.ant-space-item')[0]
            model_dict['submit_tag'] = bottom_tag.find_elements(By.CSS_SELECTOR, '.ant-space-item')[1]
            model_dict_list.append(model_dict)   
        next_page_tag = driver.find_elements(By.CSS_SELECTOR, '.ant-pagination-next')
        # 没有下一页
        if len(next_page_tag) == 1:
            break
        # 有下一页
        else:
            next_page_tag = next_page_tag[-1]

            # 看看是否可以点击     
            if next_page_tag.get_attribute('aria-disabled') == 'false':
                next_page_tag.click()
                time.sleep(1)
            else:
                break

    logger.info(f'共获取到{len(model_dict_list)}条训练产出模型信息') 

    return model_dict_list

def submit_model(driver, i, id, model_name, model_desc = "", next_page = False, next_page_cnt = 0):
    # 查看是否有uploaded_model.csv,如果没有就创建
    if not os.path.exists('data\\uploaded_model.csv'):
        with open('data\\uploaded_model.csv', 'w', newline='') as f:
            pass
    # 如果此时csv里面文件为空，则不读取
    if os.path.isfile('data\\uploaded_model.csv') and os.path.getsize('data\\uploaded_model.csv') > 0:
        uploaded_model_list = read_data('uploaded_model')
    else:
        uploaded_model_list = []

    # 转为DataFrame
    df_uploaded_model = pd.DataFrame(uploaded_model_list)

    # 请求对应页面    
    driver.get(Config.TRAIN_URL)
    # 获取正在进行的任务
    doing_task = driver.find_elements(By.CSS_SELECTOR, '.ant-spin-container .card___oQ1yu')[0]
    # 获取对应的训练任务名称
    doing_task_name = doing_task.find_elements(By.CSS_SELECTOR, '.ant-completeText-moreText-moreText')[0].text
    # 对应的按钮
    doing_botton = doing_task.find_elements(By.CSS_SELECTOR, '.ant-btn.ant-btn-default')
    # 点击按钮
    doing_botton[1].click()
    time.sleep(1)

    for j in range(next_page_cnt):
        if next_page:
            next_page_tag = driver.find_elements(By.CSS_SELECTOR, '.ant-pagination-next')
            # 没有下一页
            if len(next_page_tag) == 1:
                logger.info('没有下一页')
                return False
            # 有下一页
            else:
                next_page_tag = next_page_tag[-1]
                # 看看是否可以点击     
                if next_page_tag.get_attribute('aria-disabled') == 'false':
                    next_page_tag.click()
                    time.sleep(1)

    
    # 获取所有模型的标签
    model_tags = driver.find_elements(By.CSS_SELECTOR, '.ant-table-tbody>tr')[1:]

    #　初始化提交的模型字典
    uploaded_model_dict = {}

    # 获取指定id的模型并且提取特征码
    model_id_info = model_tags[i % 10].find_elements(By.CSS_SELECTOR, '.ant-table-cell')
    # 模型特征码
    model_speical_id = str(doing_task_name) + "-" + str(model_id_info[0].text) + "-" + str(model_id_info[1].text)
    # 提交模型
    model_id_info[3].find_elements(By.CSS_SELECTOR, '.ant-space-item')[1].click()
    # 用户自定义模型名称
    driver.find_element(By.CSS_SELECTOR, '#name').send_keys(model_name+str(id))
    # 提交信息
    driver.find_element(By.CSS_SELECTOR, '#desc').send_keys(model_desc)
    if os.path.getsize('data\\uploaded_model.csv') > 0:
        if model_speical_id in df_uploaded_model['model_speical_id'].values:
            logger.info("模型已存在")
            return False

    # 给模型字典赋值
    uploaded_model_dict['model_name'] = model_name+str(id)
    uploaded_model_dict['model_speical_id'] = model_speical_id
    uploaded_model_dict['model_desc'] = model_desc
    print(uploaded_model_dict)
    # 提交
    driver.find_element(By.CSS_SELECTOR, '.ant-btn.ant-btn-primary.ant-btn-lg.ant-btn-two-chinese-chars').click()
    # 等待提交成功
    success_flag = False
    time.sleep(2)
    if "留在" in driver.find_elements(By.CSS_SELECTOR, '.ant-btn.ant-btn-link')[-1].text:
        success_flag = True
    # 如果成功，则保存模型信息
    if success_flag:
        uploaded_model_list.append(uploaded_model_dict)
        save_data(uploaded_model_list, "uploaded_model")
        logger.info(f"模型 {model_name}{id} 提交成功")
        return True
    else:
        logger.info(f"模型 {model_name}{id} 提交失败")
        return False



def print_model_info(model_dict_list):
    # 使用pandas进行打印
    df = get_df(model_dict_list)
    print("模型信息:")
    print(df[['train_time', 'train_steps', 'model_size']])


def auto_submit_model(driver, name, train_task_file = "train_task_info"):

    doing_flag = False
    train_task_list = read_data(data_name = train_task_file)

    # 查看是否有uploaded_model.csv,如果没有就创建
    if not os.path.exists('data\\uploaded_model.csv'):
        with open('data\\uploaded_model.csv', 'w' , newline='') as f:
            pass
    # 如果此时csv里面文件为空，则不读取
    if os.path.isfile('data\\uploaded_model.csv') and os.path.getsize('data\\uploaded_model.csv') > 0:
        uploaded_model_list = read_data('uploaded_model')
    else:
        uploaded_model_list = []
    id = 0
    if uploaded_model_list:
        # 查看最后一个任务的名称
        last_name = pd.DataFrame(uploaded_model_list)['model_name'].values[-1]
        # 获取名字和id
        name_str = last_name[0]
        id = int(last_name[1:]) + 1
    # 查看是否有正在进行中的任务
    for train_task in train_task_list:
        if "进行中" in train_task['status']:
            logger.info(f"训练任务 {train_task['name_info']}[{train_task['status'][1].replace('#', '')}] 正在进行中")
            doing_flag = True

    if doing_flag:
        # get_monitor_url(driver)
        model_tags_list = get_model_list(driver)

        # 将数据保存为csv
        save_data(model_tags_list, data_name="doing_train_model")
        # 打印所有模型信息
        print_model_info(model_tags_list)

        is_next = False
         
        for i in range(len(model_tags_list)):
            if i >= 10:
                is_next = True
            submit_flag = submit_model(driver, i, id, name, next_page=is_next, next_page_cnt= int(i / 10))
            if submit_flag:
                id += 1
        return True

    else:
        logger.info("没有正在进行中的训练任务")
        return False


def get_upload_model(driver):
   # 请求模型管理页面的URL
    driver.get(Config.MODLE_URL)
    # 上传模型列表
    upload_model_list = []
    # 显示等待
    time.sleep(1)

    while True:
        # 获取模型列表
        model_tag_list = driver.find_elements(By.CSS_SELECTOR, '.ant-table-tbody>tr')
        model_tag_list = [model_tag_list[i] for i in range(1, len(model_tag_list))]
        # 遍历模型列表
        for model_tag in model_tag_list:
            # 存储模型信息的字典
            model_info_dict = {}
            # 获取模型名称
            model_name = model_tag.find_elements(By.CSS_SELECTOR, '.ant-table-cell.ant-table-cell-fix-left.ant-table-cell-fix-left-last>div>div')[0].text
            model_info_list = model_tag.find_elements(By.CSS_SELECTOR, '.ant-table-cell')
            model_info_list = [model_info_list[i].text for i in range(1, len(model_info_list))]
            # 模型状态
            model_status = model_tag.find_elements(By.CSS_SELECTOR, '.ant-tagv5')[0].text
            # 模型ID
            model_ID = model_tag.get_attribute("data-row-key")
            # 模型大小
            model_size = model_info_list[0]
            # 模型框架
            model_framework = model_info_list[1]
            # 模型算法
            model_algorithm = model_info_list[2]
            # 模型步数
            model_steps = model_info_list[3]
            # 模型训练时间
            model_train_time = model_info_list[4]
            # 模型归属
            model_owner = model_info_list[5]
            # 模型版本
            model_version = model_info_list[6]
            # 模型提交者
            model_submitter = model_info_list[7]
            # 模型创建时间
            model_create_time = model_info_list[8]

            # 将模型信息存储到字典中
            model_info_dict['model_name'] = model_name
            model_info_dict['model_status'] = model_status
            model_info_dict['model_ID'] = model_ID
            model_info_dict['model_size'] = model_size
            model_info_dict['model_framework'] = model_framework
            model_info_dict['model_algorithm'] = model_algorithm
            model_info_dict['model_steps'] = model_steps
            model_info_dict['model_train_time'] = model_train_time
            model_info_dict['model_owner'] = model_owner
            model_info_dict['model_version'] = model_version
            model_info_dict['model_submitter'] = model_submitter
            model_info_dict['model_create_time'] = model_create_time

            upload_model_list.append(model_info_dict)

        # 检测是否可以继续下一页
        next_page_tag = driver.find_element(By.CSS_SELECTOR, '.ant-pagination-next')
        if next_page_tag.get_attribute('aria-disabled') == 'false':
            next_page_tag.click()
            time.sleep(1)
        else:
            break

    logger.info(f'共获取到{len(upload_model_list)}条上传模型信息') 
    # 保存数据
    save_data(upload_model_list, 'upload_model_info')

    return upload_model_list



def start_eval(driver, model_tag, model_name):

    # 查看是否有uploaded_model.csv,如果没有就创建
    if not os.path.exists('data\\evaled_model.csv'):
        with open('evaled_model.csv', 'w', newline='') as f:
            pass
    # 如果此时csv里面文件为空，则不读取
    if os.path.isfile('data\\evaled_model.csv') and os.path.getsize('data\\evaled_model.csv') > 0:
        evaled_model_list = read_data('evaled_model')
    else:
        evaled_model_list = [] 
    
    # 点击评估的按钮
    model_tag.find_elements(By.CSS_SELECTOR, ".ant-btn.ant-btn-link")[0].click()

    error_tag = driver.find_elements(By.XPATH, f"//span[contains(text(),'同时可运行局数不足')]")
    if error_tag:
        logger.error('可用并发局数不足')
        return 'YES'
    
    # 输入任务名称
    driver.find_element(By.CSS_SELECTOR, '#name').send_keys(model_name + "/" + Config.BEST_MODEL)
    # 选择我方阵营
    driver.find_elements(By.CSS_SELECTOR, '.ant-select.ant-select-in-form-item.ant-select-multiple.ant-select-allow-clear.ant-select-show-arrow')[0].click()
    time.sleep(1)
    # 选择英雄
    heros_tag = driver.find_elements(By.CSS_SELECTOR, '.ant-select-item.ant-select-item-option')
    for hero_tag in heros_tag:
        hero_tag.click()
    # 选择完毕
    driver.find_elements(By.CSS_SELECTOR, '.ant-select.ant-select-in-form-item.ant-select-multiple.ant-select-allow-clear.ant-select-show-arrow')[0].click()
    # 选择敌方阵营的英雄
    driver.find_element(By.CSS_SELECTOR, '#rc_select_18').click()
    time.sleep(1)
    # 输入要搜索的英雄
    select_model_tag = driver.find_element(By.CSS_SELECTOR, '.ant-input-affix-wrapper.ant-input-search.ant-input-affix-wrapper-borderless')
    # 引用鼠标动作
    ActionChains(driver).click(select_model_tag).send_keys_to_element(select_model_tag, Config.BEST_MODEL).perform()
    # 根据文本选择
    driver.find_element(By.XPATH, f"//span[contains(text(),'文件大小')]").click()
    # 选择敌方阵营
    driver.find_elements(By.CSS_SELECTOR, '.ant-select.ant-select-in-form-item.ant-select-multiple.ant-select-allow-clear.ant-select-show-arrow')[1].click()
    time.sleep(1)
    # 选择英雄
    heros_tag = driver.find_elements(By.CSS_SELECTOR, '.ant-select-item.ant-select-item-option')[-3:]
    for hero_tag in heros_tag:
        hero_tag.click()
    # 选择完毕
    driver.find_elements(By.CSS_SELECTOR, '.ant-select.ant-select-in-form-item.ant-select-multiple.ant-select-allow-clear.ant-select-show-arrow')[1].click()
    # 输入评估局数
    for i in range(Config.EVALUATE_NUM):
        time.sleep(0.2)
        driver.find_elements(By.CSS_SELECTOR, '.anticon.ant-input-number-camp-icon')[0].click()
    # 开始评估
    driver.find_element(By.CSS_SELECTOR, '.ant-btn.ant-btn-primary.ant-btn-lg').click()
    time.sleep(2)

    
    success_flag = False
    if "留在" in driver.find_elements(By.CSS_SELECTOR, '.ant-btn.ant-btn-link')[-1].text:
        success_flag = True

    if success_flag:
        logger.info(f"{model_name}评估成功")
        evaled_model_dict = {}
        evaled_model_dict['model_name'] = model_name
        evaled_model_list.append(evaled_model_dict)
        save_data(evaled_model_list, "evaled_model")
        return True
    else:
        logger.info(f"{model_name}评估失败")
        return False



def eval_model(driver, eval_model_list):
    driver.get(Config.MODLE_URL)
    time.sleep(1)

    # 是否翻页
    is_next_page = False
    i = 0
    # 第一步先遍历
    while True:
        if i == len(eval_model_list):
            break

        # 如果需要翻页
        if is_next_page:
            # 检测是否可以继续下一页
            next_page_tag = driver.find_element(By.CSS_SELECTOR, '.ant-pagination-next')
            if next_page_tag.get_attribute('aria-disabled') == 'false':
                next_page_tag.click()
                time.sleep(1)

        # 寻找对应的标签
        upload_model_tags = driver.find_elements(By.CSS_SELECTOR, '.ant-table-tbody>tr')[1:]

        for j in range(len(upload_model_tags)):
            # 获取模型名称
            model_name = upload_model_tags[j].find_elements(By.CSS_SELECTOR, '.ant-table-cell.ant-table-cell-fix-left.ant-table-cell-fix-left-last>div>div')[0].text
            # 可以开始评估
            if model_name == eval_model_list[i]:
                succ_flag = start_eval(driver, upload_model_tags[j], model_name)
                if succ_flag == 'YES':
                    return True
                # 评估完成
                is_next_page = False
                i += 1
                # 重新请求
                driver.get(Config.MODLE_URL)
                time.sleep(1)
                # 结束
                break
            else:
                if j == len(upload_model_tags) - 1:
                    # 翻页
                    is_next_page = True
                continue

# 自动评估
def auto_eval(driver):
    # 查看是否有uploaded_model.csv,如果没有就创建
    if not os.path.exists('data\\evaled_model.csv'):
        with open('data\\evaled_model.csv', 'w', newline='') as f:
            pass
    # 如果此时csv里面文件为空，则不读取
    if os.path.isfile('data\\evaled_model.csv') and os.path.getsize('data\\evaled_model.csv') > 0:
        evaled_model_list = read_data('evaled_model')
    else:
        evaled_model_list = {"model_name": []} 


    # 进入评估管理页面
    driver.get(Config.MODLE_URL)
    # 读取两个csv文件来确定上传的有那些检测成功
    upload_model_info = read_data("upload_model_info")
    uploaded_model = get_df(read_data("uploaded_model"))
    # 将上传的检测成功的模型放入列表
    success_uploaded_list = []
    for upload_model in upload_model_info:
        model_name = upload_model['model_name']
        model_status = upload_model['model_status']
        if model_name in uploaded_model['model_name'].values and model_status == "检测成功" and model_name not in pd.DataFrame(evaled_model_list)['model_name'].values:
            success_uploaded_list.append(model_name)
    # 开始进行评估对应检测完成的模型
    succ_flag = eval_model(driver, success_uploaded_list)
    if succ_flag:
        logging.info(f"评估任务完成")
        return True

def shadown_root_html(driver):
    # 执行JavaScript代码获取Shadow DOM中的HTML内容
    js_code = """
        // 查询包含 shadow DOM 的元素
        const wujieApp = document.querySelector("#root > div > div:nth-child(2) > wujie-app");
        
        // 检查该元素是否存在并且是否有 shadowRoot
        if (wujieApp && wujieApp.shadowRoot) {
            // 在 shadowRoot 中查询 html 标签
            const htmlElement = wujieApp.shadowRoot.querySelector("html");
            
            // 检查 html 标签是否存在，并返回其 outerHTML
            if (htmlElement) {
                return htmlElement.outerHTML;
            }
        }
        
        // 如果没有找到 shadowRoot 或 html 标签，则返回 null
        return null;
    """
    shadow_dom_html = driver.execute_script(js_code)
    with open("shadow_dom.html", "w", encoding="utf-8") as f:   
        f.write(shadow_dom_html)
    return shadow_dom_html

def get_eval_info(driver):
    # 获取评估信息
    driver.get(Config.EVAL_URL)

    # 读取前面两个对应的csv文件
    upload_model_info = read_data("upload_model_info")
    uploaded_model = read_data("uploaded_model")
    evaled_model = read_data("evaled_model")

    # 先初始化评估信息的空列表
    eval_info_list = []

    #　检测下是否存在eval_info.csv文件，如果存在则先读取
    if os.path.exists("data\\eval_info.csv") and os.path.getsize("data\\eval_info.csv") > 0:
        eval_info_list = read_data("eval_info")
    else:
        # 如果不存在，则先创建一个空的csv文件
        with open("data\\eval_info.csv", "w", encoding="utf-8") as f:
            pass
        eval_info_list = []

    while True:
        # 取到所有评估任务的标签
        evaled_tags = driver.find_elements(By.CSS_SELECTOR, ".battleTaskItem___fe3Fb")

        for eval_tag in evaled_tags:

            # 获取评估任务的名称
            eval_name = eval_tag.find_element(By.CSS_SELECTOR, ".modelName___FZQCn").text
            # 对应的评估信息tag列表
            eval_info_tags = eval_tag.find_elements(By.CSS_SELECTOR, ".ant-tagv5")
            # 评估状态
            eval_status = eval_info_tags[0].text
            if eval_status != "已完成":
                continue
            # 评估id
            eval_id = eval_info_tags[1].text

            if eval_info_list:
                # 判断是否已经存在该评估信息
                if eval_id in get_df(eval_info_list)['eval_id'].values:
                    continue

            # 评估可见性
            eval_visibility = eval_info_tags[2].text
            # 评估版本
            eval_version = eval_info_tags[3].text
            # 评估提交人
            eval_submitter = eval_tag.find_element(By.CSS_SELECTOR, ".ant-typography.ant-typography-ellipsis.ant-typography-single-line.ant-copyableText").text
            # 评估A阵营模型
            eval_A_model = eval_tag.find_elements(By.CSS_SELECTOR, ".ant-typography.ant-typography-ellipsis.ant-typography-single-line.ant-typography-ellipsis-single-line.modelName___RuHVB")[0].text
            # 评估B阵营模型
            eval_B_model = eval_tag.find_elements(By.CSS_SELECTOR, ".ant-typography.ant-typography-ellipsis.ant-typography-single-line.ant-typography-ellipsis-single-line.modelName___RuHVB")[1].text
            # A胜利次数
            eval_A_win_count = eval_tag.find_elements(By.CSS_SELECTOR, ".value___F7456.ellipsis___ToyLS.scoreTx___uNW7T")[0].text
            # B胜利次数
            eval_B_win_count = eval_tag.find_elements(By.CSS_SELECTOR, ".value___F7456.ellipsis___ToyLS.scoreTx___uNW7T")[1].text
            # 异常次数
            eval_exception_count = eval_tag.find_elements(By.CSS_SELECTOR, ".value___F7456.ellipsis___ToyLS.scoreTx___uNW7T")[2].text
            # 总次数
            eval_total_count = eval_tag.find_elements(By.CSS_SELECTOR, ".value___F7456.ellipsis___ToyLS.scoreTx___uNW7T")[3].text
            # 查看详细页面
            eval_detail_page = eval_tag.find_element(By.CSS_SELECTOR, ".ant-btn.ant-btn-default").click()
            # 切换到新窗口
            driver.switch_to.window(driver.window_handles[-1])
            # 等待5秒
            time.sleep(5)
            # 获取隐藏的html
            shadown_html = shadown_root_html(driver)
            # 用selector获取信息
            selector = Selector(shadown_html)
            # 获取对应的信息
            divs = selector.css(".ant-table-tbody")[0]
            divs = divs.css("tr")[1:]
            # A阵营所有信息
            A_detail_info = divs[0].css(".ant-table-cell>div::text").getall()
            # B阵营所有信息
            B_detail_info = divs[1].css(".ant-table-cell>div::text").getall()
            # A阵营avg KDA
            eval_A_kda = A_detail_info[1]
            # A阵营avg 击杀
            eval_A_kill = A_detail_info[2]
            # A阵营avg 死亡
            eval_A_death = A_detail_info[3]
            # A阵营avg 经济
            eval_A_money = A_detail_info[4]
            # A阵营avg 经验
            eval_A_exp = A_detail_info[5]
            # B阵营 avg KDA
            eval_B_kda = B_detail_info[1]
            # B阵营avg 击杀
            eval_B_kill = B_detail_info[2]
            # B阵营avg 死亡
            eval_B_death = B_detail_info[3]
            # B阵营avg 经济
            eval_B_money = B_detail_info[4]
            # B阵营avg 经验
            eval_B_exp = B_detail_info[5]
            # 退回
            driver.close()
            # 换回原窗口
            driver.switch_to.window(driver.window_handles[0])

            # 存进字典
            eval_data = {
                "eval_name": eval_name,
                "eval_status" : eval_status,
                "eval_id": eval_id,
                "eval_visibility": eval_visibility,
                "eval_version": eval_version,
                "eval_submitter": eval_submitter,
                "eval_A_model": eval_A_model,
                "eval_B_model": eval_B_model,
                "eval_A_win_count": eval_A_win_count,
                "eval_B_win_count": eval_B_win_count,
                "eval_exception_count": eval_exception_count,
                "eval_total_count": eval_total_count,
                "eval_A_kda": eval_A_kda,
                "eval_A_kill": eval_A_kill,
                "eval_A_death": eval_A_death,
                "eval_A_money": eval_A_money,
                "eval_A_exp": eval_A_exp,
                "eval_B_kda": eval_B_kda,
                "eval_B_kill": eval_B_kill,
                "eval_B_death": eval_B_death,
                "eval_B_money": eval_B_money,
                "eval_B_exp": eval_B_exp

            }
            eval_info_list.append(eval_data)

        # 检测是否可以继续下一页
        next_page_tag = driver.find_element(By.CSS_SELECTOR, '.ant-pagination-next')
        if next_page_tag.get_attribute('aria-disabled') == 'false':
            next_page_tag.click()
            time.sleep(1)
        else: 
            # 保存到文件
            save_data(eval_info_list, 'eval_info')
            break

def get_output_info(eval_df, upload_df, uploaded_df, enemy_model):
    # 获取胜率信息
    eval_df["win_rate"] = eval_df['eval_A_win_count'] / (eval_df['eval_A_win_count'] + eval_df['eval_B_win_count'])
    # 处理所有信息
    upload_model_info = upload_df[['model_name', 'model_steps']]
    output_info = pd.merge(upload_model_info, eval_df, left_on='model_name', right_on='eval_A_model')
    # 对应对手
    output_info = output_info[output_info['eval_B_model'] == enemy_model]
    # modelname必须是upload_df中的而且也在
    output_info = output_info[output_info['model_name'].isin(uploaded_df.model_name)]

    return output_info[['win_rate', 'eval_A_kda', 'eval_A_money', 'eval_A_exp', 'model_steps', 'model_name']]

def get_graph(df):
    import pyecharts.options as opts
    from pyecharts.charts import Line
    from pyecharts.globals import ThemeType

    # 对数据按照 model_steps 排序
    df_sorted = df.sort_values(by='model_steps')

    model_name_list = df_sorted['model_name'].tolist()
    steps = df_sorted['model_steps'].tolist()
    x_data = [f"{name}/{step}" for name, step in zip(model_name_list, steps)]
    y_data1 = df_sorted['win_rate'].round(2).tolist()
    y_data2 = df_sorted['eval_A_kda'].tolist()
    y_data3 = [round(x / 10000, 4) for x in df_sorted['eval_A_money']]
    y_data4 = [round(x / 10000, 4) for x in df_sorted['eval_A_exp']]


    c =  (
        Line(init_opts=opts.InitOpts(theme=ThemeType.DARK, width="1200px", height="800px"))
        .add_xaxis(xaxis_data=x_data)
        .add_yaxis(
            series_name="模型胜率",
            y_axis=y_data1,
            markpoint_opts=opts.MarkPointOpts(
                data=[
                    opts.MarkPointItem(type_="max", name="最大值"),
                    opts.MarkPointItem(type_="min", name="最小值"),
                ]
            ),
            markline_opts=opts.MarkLineOpts(
                data=[opts.MarkLineItem(type_="average", name="平均值")]
            ),
        )
        .add_yaxis(
            series_name="模型KDA",
            y_axis=y_data2,
            markpoint_opts=opts.MarkPointOpts(
                data=[
                    opts.MarkPointItem(type_="max", name="最大值"),
                    opts.MarkPointItem(type_="min", name="最小值"),
                ]
            ),
            markline_opts=opts.MarkLineOpts(
                data=[opts.MarkLineItem(type_="average", name="平均值")]
            ),
        )
        .add_yaxis(
            series_name="模型经济",
            y_axis=y_data3,
            markpoint_opts=opts.MarkPointOpts(
                data=[
                    opts.MarkPointItem(type_="max", name="最大值"),
                    opts.MarkPointItem(type_="min", name="最小值"),
                ]
            ),
            markline_opts=opts.MarkLineOpts(
                data=[opts.MarkLineItem(type_="average", name="平均值")]
            ),
        )
        .add_yaxis(
            series_name="模型经验",
            y_axis=y_data4,
            markpoint_opts=opts.MarkPointOpts(
                data=[
                    opts.MarkPointItem(type_="max", name="最大值"),
                    opts.MarkPointItem(type_="min", name="最小值"),
                ]
            ),
            markline_opts=opts.MarkLineOpts(
                data=[opts.MarkLineItem(type_="average", name="平均值")]
            ),
        )
        .set_global_opts(
            title_opts=opts.TitleOpts(title="模型综合图"),
            tooltip_opts=opts.TooltipOpts(trigger="axis"),
            toolbox_opts=opts.ToolboxOpts(is_show=True),
            xaxis_opts=opts.AxisOpts(
                type_="category", 
                boundary_gap=False,
                axislabel_opts=opts.LabelOpts(rotate=0)
                ),
        )
        .render(f"output\\{Config.BEST_MODEL}_综合图.html")
    )
    return c
   
def get_output_data(enemy_model=Config.BEST_MODEL):
    eval_df = pd.read_csv('data\\eval_info.csv')
    upload_model_info = pd.read_csv('data\\upload_model_info.csv')
    uploaded_model_info = pd.read_csv('data\\uploaded_model.csv')
    output_info = get_output_info(eval_df, upload_model_info, uploaded_model_info, enemy_model)
    return output_info

output_data = get_output_data()
get_graph(output_data)

# 获取训练任务和提交对应的模型
def task1(driver, name):
    while True:
        try:
            # 获取训练任务
            logging.info("获取训练任务...")
            train_task_data = get_train_tasks(driver)
            logging.info("获取训练任务完成")
            logging.info(f"相关任务如下：{train_task_data}")
            
            # 尝试提交模型，直到成功为止
            while True:
                logging.info("提交模型...")
                succ_flag = auto_submit_model(driver, name=name)
                if succ_flag:
                    logging.info("提交模型成功")
                    break  # 成功则退出提交模型的循环
                else:
                    continue  # 失败则继续尝试提交模型
            
            break  # 成功提交后，退出最外层循环，完成任务
        except Exception as e:
            logging.error(f"任务执行失败！: {e}")
            # 捕获异常，任务将从头重试

# 获取提交模型的状态
def task2(driver):
    while True:
        try:
            # 获取模型状态
            logging.info("获取模型状态...")
            get_upload_model(driver)
            logging.info("获取模型状态完成")
            break
        
        except Exception as e:
            logging.error(f"任务执行失败！: {e}")
            # 捕获异常，任务将从头重试


# 模型评估
def task3(driver):
    while True:
        try:
            # 获取模型评估任务
            logging.info("评估模型中...")
            succ_flag = auto_eval(driver)
            logging.info("获取模型评估任务完成")
            break
        except Exception as e:
            logging.error(f"任务执行失败！: {e}")
            # 捕获异常，任务将从头重试
    

# 模型评估任务数据获取
def task4(driver):
    while True:
        try:
            # 获取模型评估任务数据
            logging.info("获取模型评估任务数据中...")
            get_eval_info(driver)
            logging.info("获取模型评估任务数据完成")
            break
        except Exception as e:
            logging.error(f"任务执行失败！: {e}")
            # 捕获异常，任务将从头重试

# 获取表现图
def task5():
    while True:
        try:
            # 获取表现图
            logging.info("获取表现图...")
            output_data = get_output_data()
            get_graph(output_data)
            logging.info("获取表现图完成")
            break
        except Exception as e:
            logging.error(f"任务执行失败！: {e}")
            # 捕获异常，任务将从头重试


def run_with_retry(driver):
    while True:
        try:
            # 记录开始时间
            start_time = datetime.now()
            logging.info(f"开始执行任务: {start_time}")
            
            # 执行任务
            task1(driver, name='D')
            time.sleep(1800)
            logger.info("等待30分钟")
            task2(driver)
            time.sleep(1)
            task3(driver)
            time.sleep(3600)
            logger.info("等待60分钟")
            time.sleep(1)
            task4(driver)
            time.sleep(1)
            task5()
            
            # 记录成功信息
            logging.info("任务执行成功")
            
        except Exception as e:
            # 记录错误信息
            logging.error(f"任务执行出错: {str(e)}")
            logging.error(f"错误详情:\n{traceback.format_exc()}")

        

if __name__ == "__main__":
    logging.info("启动定时任务程序")
    # 初始化浏览器对象
    driver = init_driver(is_show=True)
    while True:
        try:
            # 登录
            succ_flag = login(driver)
            if succ_flag:
                break
            else:
                logging.error("登录失败，重新登录中...")
            
        except Exception as e:
            logging.error(f"登录失败！: {e}")
            # 捕获异常，任务将从头重试
    run_with_retry(driver=driver)
